package com.dailyblue.example.project.bookstore.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dailyblue.example.project.bookstore.commons.bean.Book;
import com.dailyblue.example.project.bookstore.commons.bean.ShoppingCart;
import com.dailyblue.example.project.bookstore.commons.bean.User;
import com.dailyblue.example.project.bookstore.commons.config.JwtConfig;
import com.dailyblue.example.project.bookstore.commons.exception.NotLoginException;
import com.dailyblue.example.project.bookstore.commons.exception.RegisterException;
import com.dailyblue.example.project.bookstore.commons.exception.ShoppingCartIsEmptyException;
import com.dailyblue.example.project.bookstore.commons.mapper.ShoppingCartMapper;
import com.dailyblue.example.project.bookstore.commons.mapper.UserMapper;
import com.dailyblue.example.project.bookstore.commons.util.JsonResult;
import com.dailyblue.example.project.bookstore.commons.util.ResultTool;
import com.dailyblue.example.project.bookstore.commons.util.StringUtil;
import com.dailyblue.example.project.bookstore.user.feign.BookFeign;
import com.dailyblue.example.project.bookstore.user.service.UserService;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private BookFeign bookFeign;
    @Resource
    private ShoppingCartMapper shoppingCartMapper;

    @Override
    public JsonResult login(User user) {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("username", user.getUsername());
        String password = StringUtil.getMD5String(user.getPassword());
        wrapper.eq("password", password);
        log.info("user:{},pass:{},md5Pass:{}", user.getUsername(), user.getPassword(), password);
        User one = this.baseMapper.selectOne(wrapper);
        if (one == null) {
            return ResultTool.fail(401, "账号或者密码错误");
        }
        // 生成token
        String token = JwtConfig.getJwtToken(one);
        // 存放到redis中
        stringRedisTemplate.opsForValue().set("LOGIN_TOKEN_" + one.getId(), token, 1, TimeUnit.DAYS);
        return ResultTool.success(token);
    }

    @Override
    public JsonResult logout(String token) {
        log.info("开始进行登出操作！");
        if (token == null || "".equals(token)) {
            throw new NotLoginException("用户没有登陆！");
        }
        boolean b = JwtConfig.checkToken(token);
        if (!b) {
            throw new NotLoginException("登陆效果已失效！");
        }
        Claims claims = JwtConfig.parseJWT(token);
        log.info("claims:{}", claims);
        String id = claims.get("id").toString();
        stringRedisTemplate.delete("LOGIN_TOKEN_" + id);
        return ResultTool.success("登出成功");
    }

    @Override
    public JsonResult register(User user) {
        if (user == null) {
            throw new RegisterException("注册新用户失败！");
        }
        String oldPass = user.getPassword();
        String password = StringUtil.getMD5String(user.getPassword());
        user.setPassword(password);
        boolean b = save(user);
        if (!b) {
            throw new RegisterException("注册新用户失败！");
        }
        user.setPassword(oldPass);
        return login(user);
    }

    @Override
    public JsonResult addCart(String bookId, HttpServletRequest request) {
        // 获取到登陆用户的ID
        String id = request.getSession().getAttribute("USER_ID").toString();
        request.getSession().removeAttribute("USER_ID");
        HashOperations<String, String, String> hash = stringRedisTemplate.opsForHash();
        // 判断这本图书在购物车中是否存在
        Boolean is = hash.hasKey(id, bookId);
        if (is) {
            hash.increment(id, bookId, 1);
        } else {
            hash.put(id, bookId, "1");
        }
        return ResultTool.success("success");
    }

    @Override
    public JsonResult showCart(HttpServletRequest request) {
        List<ShoppingCart> list = new ArrayList<>();
        // 获取到登陆用户的ID
        String id = request.getSession().getAttribute("USER_ID").toString();
        request.getSession().removeAttribute("USER_ID");
        HashOperations<String, String, String> hash = stringRedisTemplate.opsForHash();
        Boolean hasKey = stringRedisTemplate.hasKey(id);
        if (!hasKey) {
            throw new ShoppingCartIsEmptyException("购物车为空！");
        }
        Map<String, String> map = hash.entries(id);
        Set<String> keys = map.keySet();
        keys.forEach(e -> {
            JsonResult<Book> result = bookFeign.findBookById(e);
            Book book = result.getData();
            ShoppingCart cart = new ShoppingCart();
            cart.setUserId(id);
            cart.setBookId(e);
            cart.setNumber(Integer.parseInt(map.get(e)));
            cart.setName(book.getName());
            cart.setPrice(book.getPrice());
            cart.setAuthor(book.getAuthor());
            cart.setPublisher(book.getPublisher());
            cart.setDescription(book.getDescription());
            cart.setImageName(book.getImageName());
            cart.setCreateTime(book.getCreateTime());
            cart.setUpdateTime(book.getUpdateTime());
            list.add(cart);
        });
        return ResultTool.success(list);
    }

    @Override
    public JsonResult changeCartNumber(String bookId, HttpServletRequest request, int number) {
        // 获取到登陆用户的ID
        String id = request.getSession().getAttribute("USER_ID").toString();
        HashOperations<String, String, String> hash = stringRedisTemplate.opsForHash();
        hash.increment(id, bookId, number);
        String val = hash.get(id, bookId);
        if (val != null && val.equals("0")) {
            hash.delete(id, bookId);
        }
        return showCart(request);
    }

    @Override
    public JsonResult changeCartNumber2(String bookId, HttpServletRequest request, int number) {
        // 获取到登陆用户的ID
        String id = request.getSession().getAttribute("USER_ID").toString();
        HashOperations<String, String, String> hash = stringRedisTemplate.opsForHash();
        if (number == 0) {
            hash.delete(id, bookId);
        } else {
            hash.put(id, bookId, number + "");
        }
        return showCart(request);
    }

    @Override
    public JsonResult deleteCartById(HttpServletRequest request, String... bookIds) {
        // 获取到登陆用户的ID
        String id = request.getSession().getAttribute("USER_ID").toString();
        HashOperations<String, String, String> hash = stringRedisTemplate.opsForHash();
        hash.delete(id, bookIds);
        return showCart(request);
    }

    // 同步mysql和redis中购物车信息  通过定时器
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = SQLException.class)
    public JsonResult syncShoppingCart() {
        HashOperations<String, String, String> hash = stringRedisTemplate.opsForHash();
        // 获取到所有的key
        Set<String> keys = stringRedisTemplate.keys("*");
        // 获取到hash类的key,目前所有在hash中存放的都是购物车
        Map<String, Map<String, String>> map = new HashMap<>();
        keys.forEach(e -> {
            DataType type = stringRedisTemplate.type(e);
            if (type == DataType.HASH) {
                map.put(e, hash.entries(e));
            }
        });
        // 同步数据库
        // 1. 删除购物车数据库中的所有数据
        shoppingCartMapper.clearCart();
        // 2. 将新数据添加到数据库中
        Set<String> strings = map.keySet();
        strings.forEach(userId -> {
            Map<String, String> m = map.get(userId);
            Set<String> strings1 = m.keySet();
            strings1.forEach(bookId -> {
                String number = m.get(bookId);
                JsonResult<Book> result = bookFeign.findBookById(bookId);
                Book book = result.getData();
                ShoppingCart cart = new ShoppingCart();
                cart.setUserId(userId);
                cart.setBookId(bookId);
                cart.setNumber(Integer.parseInt(number));
                cart.setName(book.getName());
                cart.setPrice(book.getPrice());
                cart.setAuthor(book.getAuthor());
                cart.setPublisher(book.getPublisher());
                cart.setDescription(book.getDescription());
                cart.setImageName(book.getImageName());
                shoppingCartMapper.insert(cart);
            });
        });
        return ResultTool.success("success");
    }
}
